﻿using System;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using EasyHook;
using MassPokerTool.Infrastructure.Hooking.Data;
using MassPokerTool.Infrastructure.Hooking.Filters;

namespace MassPokerTool.Infrastructure.Hooking.Hooks
{
    public class BitBltHook : HookBase
    {
        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
        public delegate bool DBitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int rasterOp);

        private readonly ReadOnlyCollection<IBitBltFilter> _filters;

        public BitBltHook(ReadOnlyCollection<IBitBltFilter> filters)
        {
            _filters = filters;
        }

        protected override LocalHook CreateHookInternal(object callback)
        {
            var hook = LocalHook.Create(
                LocalHook.GetProcAddress("gdi32.dll", "BitBlt"),
                new DBitBlt(BitBltHooked),
                callback);
            hook.ThreadACL.SetExclusiveACL(new[] { 0 });
            return hook;
        }

        private bool BitBltHooked(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int rasterOp)
        {
            var callback = (IQueueableEntryPoint)HookRuntimeInfo.Callback;
            try
            {
                foreach (var bitBltFilter in _filters)
                {
                    IHookData hookData;
                    if (bitBltFilter.Match(hdcDest, xDest, yDest, wDest, hDest, hdcSource, xSrc, ySrc, rasterOp,
                                           out hookData))
                    {
                        lock (callback.Queue)
                            callback.Queue.Enqueue(hookData);
                    }
                }
            }
            catch (Exception e)
            {
                lock (callback.Queue)
                {
                    callback.Queue.Enqueue(new ErrorHookData() {Exception = e});
                }
            }
            return PlatformInvoke.BitBlt(hdcDest, xDest, yDest, wDest, hDest, hdcSource, xSrc, ySrc, rasterOp);
        }
    }
}
